/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  pinyin_search_idfile.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2017/06/04
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "pinyin_search_idfile.h"

#include <core/strings/string_convert.h>
#include <core/system/memory.h>

#include <QDir>
#include <QStandardPaths>
#include <QTemporaryFile>

#include "pinyin_search_set.h"

#ifdef WIN32
#include <windows.h>
#else
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

#ifndef BASSERTS
#define BASSERTS  //
#endif
#ifndef BASSERT
#define BASSERT  //
#endif
#ifndef BLOGERR
#define BLOGERR  //
#endif
#ifndef BLOGINFO
#define BLOGINFO  //
#endif
#ifndef BLOGDEBUG
#define BLOGDEBUG  //
#endif

#define MAX_PATH 260

namespace qb {
namespace base {
namespace internal {
class SingleMappingView {  // 映射视图
  int m_size;              // 视图大小，通常都是view size的倍数
  int m_start;   // 在文件中的偏移量,是系统PAGE_SIZE的整数倍
  char* m_view;  // 映射好的内存，后续需要解决使用试用一个内存地址去映射
  char* m_last;  // 上一个m_view指针,用于绑定一个内存位置,反复使用
  unsigned long m_granularity;  // 系统内存分配粒度
  u64 m_acquie_count;           // Acquire调用次数
  SingleMappingView* m_next;
  SingleMappingView* m_prev;
  friend class MultiMappingView;

 public:
  SingleMappingView()
      : m_size(0),
        m_start(0),
        m_view(0),
        m_last(0),
        m_granularity(64 * 1024),
        m_acquie_count(0),
        m_next(nullptr),
        m_prev(nullptr) {
    m_granularity = qb::base::getMemPageSize();
  }
  ~SingleMappingView() { Release(); }

 protected:
  int GetCenter() const { return m_start + m_size / 2; }
  void SetNext(SingleMappingView* next) { m_next = next; }
  void SetPrev(SingleMappingView* prev) { m_prev = prev; }
  SingleMappingView* GetNext() const { return m_next; }
  SingleMappingView* GetPrev() const { return m_prev; }

 public:  // 统计用
  u64 GetAcquireCount() const { return m_acquie_count; }
  int GetSize() const { return m_size; }
  int GetBytes() const { return m_view ? m_size : 0; }

 private:  // 统计用
  bool Contains(int offset, int bytes) const {
    return m_view && offset >= m_start && offset + bytes < m_start + m_size;
  }

  bool Acquire(void* mapping, int start, int size) {
    BASSERT(mapping != NULL);
    m_start = start;
    m_size = size;
    m_acquie_count++;
#ifdef WIN32
    if (m_last)
      m_view = (char*)MapViewOfFileEx(mapping, FILE_MAP_READ | FILE_MAP_WRITE,
                                      0, start, size, m_last);
    if (m_view == nullptr)
      m_view = (char*)MapViewOfFileEx(mapping, FILE_MAP_READ | FILE_MAP_WRITE,
                                      0, start, size, 0);

    if (m_view == nullptr) {
      BASSERTS(0, "start=%8d size=%d last:0x%8x, error:%d", start, size, m_last,
               GetLastError());
      // BVIEW("SSMapViewOfFile failed ,error:%d", GetLastError());
    }
#else
    m_view = (char*)((char*)mapping + start);
#endif
    return m_view != NULL;
  }

  char* GetMemory(int offset, int bytes) {
    BASSERT(m_view != nullptr);
    BASSERT(offset >= m_start && offset + bytes <= m_start + m_size);
    if (Contains(offset, bytes)) return m_view + (offset - m_start);
    return nullptr;
  }

 public:
  void Release() {
    m_last = m_view;
    if (m_view != NULL) {
#ifdef WIN32
      UnmapViewOfFile(m_view);
#endif
      /*MEMORY_BASIC_INFORMATION mbi;
      memset(&mbi, 0, sizeof(mbi));
      VirtualQuery(m_view, &mbi, sizeof(mbi));*/
    }
    m_view = NULL;
  }

  char* ReadMemory(void* mapping, int pagesize, int offset, int bytes) {
    // 从映射文件mapping中读取指定位置的内存
    // 读取到的内存只保证当时有效,下一次可能被移除
    BASSERT(mapping != NULL && pagesize % m_granularity == 0);
    if (!Contains(offset, bytes)) {
      int index = offset / pagesize;
      int start = index * pagesize;
      int size = offset + bytes - start;
      size = ROUND_ALIGN(size, pagesize);
      Release();
      Acquire(mapping, start, size);
    }
    return GetMemory(offset, bytes);
  }
};

class MultiMappingView {  // 1主多附
  using MappingView = SingleMappingView;
  enum { MAX_VIEW = 8 };
  struct SlotInfo {
    MappingView* head;
  };
  MappingView* m_last_view;
  int m_view_count;
  int m_max_count;
  SlotInfo m_views[MAX_VIEW];

 public:
  MultiMappingView() : m_last_view(nullptr), m_view_count(0), m_max_count(4) {
    memset(m_views, 0, sizeof(SlotInfo) * MAX_VIEW);
  }
  ~MultiMappingView() {}

 public:
  u64 GetAcquireCount() const {
    u64 ncount = 0;
    for (int i = 0; i < MAX_VIEW; i++) {
      MappingView* head = m_views[i].head;
      while (head) {
        ncount += head->GetAcquireCount();
        head = head->GetNext();
      }
    }
    return ncount;
  }
  int GetSize() const { return 0; }
  int GetBytes() const {
    int bytes = 0;
    return bytes;
  }

 public:
  void Release() {
    // 外界需要销毁,或者扩大映射文件
    for (int i = 0; i < MAX_VIEW; i++) {
      MappingView* head = m_views[i].head;
      while (head) {
        head->Release();
        head = head->GetNext();
      }
    }
    m_last_view = nullptr;
  }

  char* ReadMemory(void* mapping, int pagesize, int offset, int bytes) {
    // 1.使用上一个
    if (m_last_view && m_last_view->Contains(offset, bytes))
      return m_last_view->ReadMemory(mapping, pagesize, offset, bytes);

    // 2.通过offset找到一个
    int index = offset / pagesize;
    int slot = index % MAX_VIEW;
    MappingView* head = m_views[slot].head;
    while (head && head->Contains(offset, bytes)) {
      if (head->Contains(offset, bytes)) {
        m_last_view = head;
        return m_last_view->ReadMemory(mapping, pagesize, offset, bytes);
      }
      head = head->GetNext();
    }

    // 3.新建一个
    if (m_view_count < m_max_count) {
      MappingView* view = new MappingView();
      view->SetNext(m_views[slot].head);
      if (m_views[slot].head) m_views[slot].head->SetPrev(view);
      m_views[slot].head = view;

      char* mem = view->ReadMemory(mapping, pagesize, offset, bytes);

      m_view_count++;
      m_last_view = view;
      return mem;
    }

    // 4.遍历找到一个,重新映射,使用与offset距离最远的
    MappingView* found = nullptr;
    int maxdist = -1;
    int foundslot = -1;
    for (int i = 0; i < MAX_VIEW; i++) {
      MappingView* head = m_views[i].head;
      if (head == nullptr) continue;
      int dist = head->GetCenter() - offset;
      dist = dist < 0 ? (-dist) : dist;
      if (dist > maxdist) {
        maxdist = dist;
        found = head;
        foundslot = i;
      }
    }

    BASSERT(found != nullptr && found == m_views[foundslot].head);
    if (foundslot != slot) {
      SlotInfo& oldslot = m_views[foundslot];
      SlotInfo& newslot = m_views[slot];

      oldslot.head = found->GetNext();
      if (oldslot.head) oldslot.head->SetPrev(nullptr);

      BASSERT(found->GetPrev() == nullptr);
      found->SetNext(newslot.head);
      if (newslot.head) newslot.head->SetPrev(found);
      newslot.head = found;
    }
    m_last_view = found;

    char* mem = found->ReadMemory(mapping, pagesize, offset, bytes);
    return mem;
  }
};

IndexIdFile::IndexIdFile(int npageCount)
    : m_reserve_ratio(0.4f),
      m_align_size(256),
      m_cur_filesize(0),
      m_cur_usedsize(0),
      m_view(new MappingView) {
#ifdef WIN32
  m_file = NULL;
#else
  m_file = -1;
#endif
  m_mapping = NULL;
  npageCount = npageCount <= 0 ? 1 : npageCount;
  m_sys_pagesize = qb::base::getMemPageSize() * npageCount;
  BASSERT(m_sys_pagesize > 0);

  m_increase_size = ROUND_ALIGN(MEGA_BYTES * 4, MEGA_BYTES);
  if (m_increase_size % m_sys_pagesize != 0)
    m_increase_size = ROUND_ALIGN(m_increase_size, m_sys_pagesize);

  QString strFileName = QDir::tempPath() + QDir::separator() + QString("QBS");
  QTemporaryFile tmpFile(strFileName);
  tmpFile.open();
  tmpFile.close();
  m_fname = strFileName.toStdString();
  Initialize(m_fname.c_str());
}

IndexIdFile::~IndexIdFile() {
  m_view->Release();

#ifdef WIN32
  if (m_mapping != NULL) {
    CloseHandle(m_mapping);
    m_mapping = NULL;
  }
  if (m_file != NULL) {
    SetFilePointer(m_file, 0, 0, FILE_BEGIN);
    SetEndOfFile(m_file);
    CloseHandle(m_file);
  }
  DeleteFileA(m_fname.c_str());
#else
  if (m_mapping) {
    munmap(m_mapping, m_cur_filesize);
    m_mapping = NULL;
  }

  if (m_file != -1) {
    if (m_file) {
      ftruncate(m_file, 0);
    }
    close(m_file);
    m_file = -1;
  }

  if (!m_fname.empty()) {
    remove(m_fname.c_str());
  }
#endif
}

bool IndexIdFile::Initialize(const char* fname) {
#ifdef WIN32
  unsigned long fileattr = FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH;
  fileattr |= FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
  m_file =
      CreateFileA(fname, GENERIC_WRITE | GENERIC_READ, 0 /*FILE_SHARE_READ*/,
                  NULL, OPEN_ALWAYS, fileattr, NULL);
  if (m_file == INVALID_HANDLE_VALUE || m_file == NULL) {
    BLOGERR("[SSBASE]failed to open file:%s error:%d", fname, GetLastError());
    return false;
  }
  SetFilePointer(m_file, 0, 0, FILE_BEGIN);
  SetEndOfFile(m_file);
#else
  m_file = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
  if (m_file == -1) {
    return false;
  }

  ftruncate(m_file, 0);
#endif
  return true;
}

bool IndexIdFile::PrepareMapping(int maxSize) {
  BASSERT(maxSize % m_increase_size == 0);
  m_cur_filesize = maxSize;

  BASSERT(m_mapping == NULL);
#ifdef WIN32
  unsigned long flProtect = PAGE_READWRITE | SEC_COMMIT;
  m_mapping =
      CreateFileMapping(m_file, NULL, flProtect, 0, m_cur_filesize, NULL);
#else
  ftruncate(m_file, m_cur_filesize);
  m_mapping =
      mmap(0, m_cur_filesize, PROT_READ | PROT_WRITE, MAP_SHARED, m_file, 0);
#endif
  if (m_mapping == NULL) return false;

  return true;
}
void IndexIdFile::FreeSpace(void* memory, int offset, int bytes) {
  // 映射文件不需要删除
  m_keyheads.Free(memory, offset, bytes);
}
void* IndexIdFile::AllocByMapping(int bytes) {
  // if (m_check_mapping && bytes % 2 == 0)
  // return NULL;
  // 扩张映射文件
  while (m_cur_usedsize + bytes > m_cur_filesize) {
    m_view->Release();
#ifdef WIN32
    CloseHandle(m_mapping);
    m_mapping = NULL;
#else
    if (m_mapping) {
      munmap(m_mapping, m_cur_filesize);
      m_mapping = NULL;
    }
#endif

    if (!PrepareMapping(m_cur_filesize + m_increase_size)) {
#ifdef WIN32
      BASSERTS(0,
               "PrepareMapping failed,bytes:%d offset:%d filesize:%d "
               "increase_size:%d,error:%d",
               bytes, m_cur_usedsize, m_cur_filesize, m_increase_size,
               ::GetLastError());
#endif
      return NULL;
    }
  }

  BASSERTS(m_cur_filesize % m_sys_pagesize == 0, "filesize:%d  pagesize:%d",
           m_cur_filesize, m_sys_pagesize);

  return m_view->ReadMemory(m_mapping, m_sys_pagesize, m_cur_usedsize, bytes);

  // int index = m_cur_usedsize / m_sys_pagesize;
  // int start = index*m_sys_pagesize;
  // int size = m_cur_usedsize + bytes - start;
  // size = ROUND_ALIGN(size, m_sys_pagesize);
  // BASSERTS(start >= 0 && start + size <= m_cur_filesize, "end:%8x  max:%8x",
  // start + size, m_cur_filesize); BASSERTS(start%m_sys_pagesize == 0 &&
  // size%m_sys_pagesize==0, "end:%8x  max:%8x", start + size, m_cur_filesize);
  // m_view->Release();
  // m_view->Acquire(m_mapping, start, size);
  // return m_view->GetMemory(m_cur_usedsize, bytes);

  // char* mem = (char*)m_view->GetView();
  // if (mem)
  //{
  //	mem += m_cur_usedsize - start;
  //	memset(mem, 0, bytes);
  //	return mem;
  // }
  // return NULL;
}
void* IndexIdFile::AllocSpace(int bytes, int& offset) {
  BASSERT(bytes % m_align_size == 0 && bytes > 0);
  char* mem = (char*)AllocByMapping(bytes);
  if (mem == NULL) {
    mem = (char*)m_keyheads.AllocHead(m_cur_usedsize, bytes);
  }
  offset = m_cur_usedsize;
  m_cur_usedsize += bytes;
  return mem;
}

internal::IndexIdFile::KeyHead* IndexIdFile::PrepareSpace(int offset,
                                                          int bytes) {
  // 使得从偏移量offset开始的长度为bytes字节的内存可用
  BASSERT(offset >= 0 && bytes % m_align_size == 0 && bytes > 0 &&
          offset + bytes <= m_cur_usedsize);
  KeyHead* head = m_keyheads.GetHead(offset, bytes);
  if (head) return head;

  head = (KeyHead*)m_view->ReadMemory(m_mapping, m_sys_pagesize, offset, bytes);

  // if (!m_view->Contains(offset, bytes))
  //{
  //	m_view->Release();
  //	int start = ROUND_ALIGN(offset, m_sys_pagesize);
  //	if (start > offset)start -= m_sys_pagesize;
  //	int size = offset + bytes - start;
  //	size = ROUND_ALIGN(size, m_sys_pagesize);
  //	m_view->Acquire(m_mapping, start, size);
  // }
  ////BASSERT(m_view->GetView() != NULL);
  ////head = (KeyHead*)(m_view->GetView() + (offset - m_view->GetStart()));
  // head = (KeyHead*)(m_view->GetMemory(offset, bytes));
  BASSERT(head &&
          /*head->m_signature == SIGNATURE&&*/ head->m_offset == offset &&
          head->m_bytes == bytes);
  return head;
}
int IndexIdFile::GetMemoryBytes() const {
  int bytes = sizeof(IndexIdFile);
  bytes += m_view->GetBytes();
  bytes += m_blocks.size() * sizeof(std::map<int, Block>::value_type);
  bytes += m_fname.size();
  bytes += m_keyheads.GetMemoryBytes();
  return bytes;
}
void IndexIdFile::Dump() const {
  BLOGDEBUG(
      "[PINYIN]view:%8d blocks:%8d keyhead:%8d  filesize:%12d  usedsize:%12d "
      "acquire:%I64d",
      m_view->GetSize(), m_blocks.size(), m_keyheads.GetSize(), m_cur_filesize,
      m_cur_usedsize, m_view->GetAcquireCount());
}
void IndexIdFile::Update(const InverseKey& key, IndexIdRange* iirange) {
  // 固定头部+变长记录+适度扩展区域40%
  // 当m_memory_set为0的时候,是pinyin_search_engine.cpp中UseMappingSet为true的分支
  // 表明所有的map<int,char>数据不在内存,而是存在于映射文件中,此时相当于new
  // map<int,char>,默认初始尺寸是16
  BASSERT(iirange);
  int nRealSize = iirange->m_memory_set ? iirange->m_memory_set->size() : 0;
  int nDefaultSize = nRealSize <= 0 ? 16 : nRealSize;
  int bytes = sizeof(KeyHead) +
              (int)nDefaultSize * sizeof(IndexId) * (1 + m_reserve_ratio);
  bytes = ROUND_ALIGN(bytes, m_align_size);
  int offset = 0;
  void* memory = AllocSpace(bytes, offset);
  if (memory == NULL) {
    BLOGERR("[SSBASE]AllocSpace(%d,%d) failed,keyheads:%d", bytes, offset,
            m_keyheads.GetSize());
    return;
  }

  KeyHead* head = (KeyHead*)memory;
  head->m_offset = offset;
  head->m_signature = SIGNATURE;
  head->m_length = nRealSize;
  head->m_used = head->m_length * sizeof(IndexId);
  head->m_bytes = bytes;
  head->m_capcity = (bytes - sizeof(KeyHead)) / sizeof(IndexId);
  memcpy(head->m_key, key.code, sizeof(key));

  if (iirange->m_memory_set) {
    IndexIdVec iivec((IndexId*)(head + 1), iirange->m_memory_set->size(),
                     head->m_capcity);
    iivec.copyfrom(*iirange->m_memory_set);
  }

  // m_blocks[head->m_offset] = Block(head->m_bytes, true);
  iirange->m_bytes = head->m_bytes;
  iirange->m_offset = head->m_offset;
  iirange->m_length = head->m_length;
}

bool IndexIdFile::AddKey(IndexIdRange* iirange, int key) {
  BASSERT(iirange && iirange->m_bytes > 0 && iirange->m_offset >= 0 &&
          key >= 0);
  void* memory = PrepareSpace(iirange->m_offset, iirange->m_bytes);
  if (memory == NULL) {
    BLOGERR("[SSBASE]PrepareSpace(%d,%d) failed", iirange->m_offset,
            iirange->m_bytes);
    return false;
  }
  KeyHead* head = (KeyHead*)memory;
  BASSERT(head->m_signature == SIGNATURE &&
          head->m_offset == iirange->m_offset &&
          head->m_bytes % m_align_size == 0);
  IndexIdVec iivec((IndexId*)(head + 1), head->m_length, head->m_capcity);
  if (!iivec.add(key)) {
    FreeSpace(memory, iirange->m_offset, iirange->m_bytes);
    BASSERT(head->m_length >= head->m_capcity);
    int bytes = head->m_bytes + (head->m_bytes >> 1);
    bytes = ROUND_ALIGN(bytes, m_align_size);
    int offset = 0;
    KeyHead oldhead = *head;
    void* temp = new char[oldhead.m_bytes];
    memcpy(temp, memory, oldhead.m_bytes);
    void* newmemory = AllocSpace(bytes, offset);
    if (newmemory == NULL) {
      BLOGERR("[SSBASE]AllocSpace(%d,%d) failed,keyheads:%d", bytes, offset,
              m_keyheads.GetSize());
      return false;
    }
    memcpy(newmemory, temp, oldhead.m_bytes);
    delete[] (char*)temp;
    // m_blocks[oldhead.m_offset] = Block(oldhead.m_bytes, false);

    head = (KeyHead*)newmemory;
    head->m_offset = offset;
    head->m_signature = SIGNATURE;
    head->m_bytes = bytes;
    head->m_capcity = (head->m_bytes - sizeof(KeyHead)) / sizeof(IndexId);
    iivec.bind((IndexId*)(head + 1), head->m_length, head->m_capcity);
    bool badd = iivec.add(key);
    BASSERT(badd);
    // m_blocks[head->m_offset] = Block(head->m_bytes, true);
  }
  head->m_length = iivec.size();
  head->m_used = head->m_length * sizeof(IndexId);
  iirange->m_offset = head->m_offset;
  iirange->m_bytes = head->m_bytes;
  iirange->m_length = iivec.size();
  BASSERT(head->m_used <= head->m_bytes && head->m_length <= head->m_capcity);
  return true;
}

bool IndexIdFile::RemoveKey(IndexIdRange* iirange, int key) {
  BASSERT(iirange && iirange->m_bytes > 0 && key >= 0);
  void* memory = PrepareSpace(iirange->m_offset, iirange->m_bytes);
  if (memory == NULL) {
    BLOGERR("[SSBASE]PrepareSpace(%d,%d) failed", iirange->m_offset,
            iirange->m_bytes);
    return false;
  }
  KeyHead* head = (KeyHead*)memory;
  BASSERT(head->m_signature == SIGNATURE &&
          head->m_offset == iirange->m_offset &&
          head->m_bytes % m_align_size == 0);
  IndexIdVec iivec((IndexId*)(head + 1), head->m_length, head->m_capcity);
  iivec.remove(key);
  head->m_length = iivec.size();
  head->m_used = head->m_length * sizeof(IndexId);
  iirange->m_length = iivec.size();
  BASSERT(head->m_used <= head->m_bytes && head->m_length <= head->m_capcity);
  return true;
}

bool IndexIdFile::GetIndexIdVec(const IndexIdRange* iirange,
                                IndexIdVec& iivec) {
  BASSERT(iirange && iirange->m_bytes > 0 && iirange->m_length >= 0);
  void* memory = PrepareSpace(iirange->m_offset, iirange->m_bytes);
  if (memory == NULL) {
    BLOGERR("[SSBASE]PrepareSpace(%d,%d) failed", iirange->m_offset,
            iirange->m_bytes);
    return false;
  }
  KeyHead* head = (KeyHead*)memory;
  BASSERT(head->m_offset == iirange->m_offset &&
          head->m_bytes % m_align_size == 0);
  BASSERT(head->m_length == iirange->m_length &&
          head->m_bytes == iirange->m_bytes &&
          iirange->m_offset == head->m_offset);
  iivec.bind((IndexId*)(head + 1), head->m_length, head->m_capcity);
  return true;
}

IndexIdFile::KeyHeadMemory::~KeyHeadMemory() {
  for (BackupMemory::iterator it = m_memory.begin(); it != m_memory.end();
       ++it) {
    KeyHead* head = it->second;
    if (head) {
      delete[] ((char*)head);
    }
  }
  m_memory.clear();
}

bool IndexIdFile::KeyHeadMemory::Free(void* memory, int offset, int bytes) {
  if (m_memory.empty()) return false;
  BackupMemory::iterator it = m_memory.find(memory);
  if (it != m_memory.end()) {
    OffsetMap::iterator oit = m_offsets.find(offset);
    KeyHead* ohead = oit != m_offsets.end() ? (oit->second) : NULL;
    KeyHead* head = it->second;
    BASSERT(ohead == head);
    if (head) {
      BASSERT(head->m_offset == offset);
      BASSERT(head->m_bytes == bytes);
      BASSERT(head->m_signature == SIGNATURE);
      char* ptr = (char*)(it->second);
      delete[] ptr;
    }
    m_memory.erase(it);
    if (ohead) m_offsets.erase(oit);
    return true;
  }
  return false;
}

qb::base::internal::IndexIdFile::KeyHead* IndexIdFile::KeyHeadMemory::GetHead(
    int offset, int bytes) {
  if (m_offsets.empty()) return NULL;
  OffsetMap::iterator it = m_offsets.find(offset);
  KeyHead* head = it != m_offsets.end() ? (it->second) : NULL;
  if (head) {
    BASSERT(head->m_bytes == bytes);
    BASSERT(head->m_signature == SIGNATURE);
  }
  return head;
}

qb::base::internal::IndexIdFile::KeyHead* IndexIdFile::KeyHeadMemory::AllocHead(
    int offset, int bytes) {
  if (bytes <= sizeof(KeyHead)) return NULL;
  char* buf = new char[bytes];
  memset(buf, 0, bytes);
  KeyHead* head = (KeyHead*)buf;
  head->m_offset = offset;
  head->m_signature = SIGNATURE;
  head->m_bytes = bytes;
  m_memory[buf] = head;
  m_offsets[offset] = head;
  BASSERT(m_offsets.size() == m_memory.size());
  return head;
}
int IndexIdFile::KeyHeadMemory::GetMemoryBytes() const {
  int bytes = m_memory.size() * sizeof(BackupMemory::value_type);
  bytes += m_offsets.size() * sizeof(OffsetMap::value_type);
  for (BackupMemory::const_iterator it = m_memory.begin(); it != m_memory.end();
       ++it) {
    const KeyHead* phead = it->second;
    if (phead) bytes += phead->m_bytes;
  }
  return bytes;
}

}  // namespace internal
}  // namespace base
}  // namespace qb
